using DotNet.Utilities.ConsoleHelper;
using PropertyChanged;
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using DotNet.Utilities;
using DotNet.Utilities.WindowsApi;
using WPFPractice.Utils;
using WPFTemplateLib.WpfHelpers;
/*
 * 参考自：https://www.cnblogs.com/sparkdev/p/5932429.html
 */
namespace WPFPractice
{
    public partial class WinMsiInstaller : Window
    {
        private MsiInstallerViewModel _vm;

        public WinMsiInstaller()
        {
            InitializeComponent();
            DataContext = _vm = new MsiInstallerViewModel();
        }
    }

    [AddINotifyPropertyChangedInterface]
    public class MsiInstallerViewModel : MyViewModelBase
    {
        public MsiInstallerViewModel()
        {
            SetCommandMethod();

            InitValue();

            Console.SetOut(new ConsoleWriter(s =>
            {
                ShowInfo(s);
            }));
        }

        #region 成员

        private MyInstaller _installer = null;
        private BackgroundWorker _installerBGWorker = new BackgroundWorker();

        #endregion

        #region Bindable

        /// <summary>
        /// 文件路径
        /// </summary>
        public string FilePath { get; set; }

        /// <summary>
        /// 安装参数
        /// </summary>
        public string Param { get; set; } = "ACTION=INSTALL";

        #endregion

        #region Command

        /// <summary>
        /// 命令：选择文件
        /// </summary>
        public ICommand ChooseFileCmd { get; set; }

        /// <summary>
        /// 命令：开始安装
        /// </summary>
        public ICommand StartInstallCmd { get; set; }

        /// <summary>
        /// 命令：停止安装
        /// </summary>
        public ICommand CancelInstallCmd { get; set; }

        #endregion

        /// <summary>
        /// 数据初始化
        /// </summary>
        private void InitValue()
        {
            ShowInfo("C# 执行安装 Msi 程序包");

            _installer = new MyInstaller();

            _installerBGWorker.WorkerReportsProgress = true;
            _installerBGWorker.WorkerSupportsCancellation = true;

            _installerBGWorker.DoWork += _installerBGWorker_DoWork;
            _installerBGWorker.RunWorkerCompleted += _installerBGWorker_RunWorkerCompleted;
            _installerBGWorker.ProgressChanged += _installerBGWorker_ProgressChanged;
        }

        /// <summary>
        /// 命令方法赋值(在构造函数中调用)
        /// </summary>
        private void SetCommandMethod()
        {
            ChooseFileCmd ??= new RelayCommand(o => true, o =>
            {
                Microsoft.Win32.OpenFileDialog dialog = new Microsoft.Win32.OpenFileDialog { Filter = "安装包|*.msi;*.exe" };
                if (dialog.ShowDialog() == true)
                {
                    FilePath = dialog.FileName;
                }
            });

            StartInstallCmd ??= new RelayCommand(o => true, o =>
            {
                if (string.IsNullOrWhiteSpace(FilePath))
                {
                    ShowInfo($"请先选择安装包");
                    return;
                }

                _installerBGWorker.RunWorkerAsync();
            });

            CancelInstallCmd ??= new RelayCommand(o => true, o =>
            {
                _installer.Canceled = true;
                _installerBGWorker.CancelAsync();
            });
        }

        #region 方法

        private void _installerBGWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // 消息通过 e.UserState 传回，并显示在窗口上
            string message = e.UserState.ToString();
            ShowInfo($"{message}");
        }

        private void _installerBGWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // 安装过程结束
            ShowInfo($"安装完成！");
        }

        private void _installerBGWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker bgWorker = sender as BackgroundWorker;

            // 开始执行安装方法
            _installer = new MyInstaller();
            string msiFilePath = FilePath; // msi file path
            int result = _installer.Install(bgWorker, msiFilePath, Param, out string msg);
            ShowInfo($"安装结果：{result} {msg}");
        }

        #endregion
    }

    internal class MyInstaller
    {
        private BackgroundWorker _bgWorker = null;

        public bool Canceled { get; set; }

        public int Install(BackgroundWorker bgWorker, string msiFileName, string param, out string msg)
        {
            _bgWorker = bgWorker;
            MsiHelper.MyMsiInstallUIHandler oldHandler = null;
            msg = string.Empty;

            try
            {
                string logPath = "install.log";
                MsiHelper.MsiEnableLog(MsiHelper.LogMode.Verbose, logPath, 0u);
                MsiHelper.MsiSetInternalUI(2, IntPtr.Zero);

                oldHandler = MsiHelper.MsiSetExternalUI(new MsiHelper.MyMsiInstallUIHandler(MsiProgressHandler),
                    MsiHelper.LogMode.ExternalUI,
                    IntPtr.Zero);

                //string param = "ACTION=INSTALL";
                _bgWorker.ReportProgress(0, "正在安装 ...");
                int result = (int)MsiHelper.MsiInstallProduct(msiFileName, param);
                msg = ((MsiHelper.MsiErrorCode)result).GetEnumDescription();
                
                return result;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"安装过程中异常：{ex}");
                return -1;
            }
            finally
            {
                // 一定要把默认的handler设回去。
                if (oldHandler != null)
                {
                    MsiHelper.MsiSetExternalUI(oldHandler, MsiHelper.LogMode.None, IntPtr.Zero);
                }
            }
        }

        //最重要的就是这个方法了，这里仅演示了如何cancel一个安装，更多详情请参考MSDN文档
        private int MsiProgressHandler(IntPtr context, int messageType, string message)
        {
            if (Canceled)
            {
                if (_bgWorker != null)
                {
                    _bgWorker.ReportProgress(0, "正在取消安装 ...");
                }
                // 这个返回值会告诉msi, cancel当前的安装
                return 2;
            }
            return 1;
        }
    }
}
